﻿/********************************************************
 *  ██████╗  ██████╗████████╗██╗
 * ██╔════╝ ██╔════╝╚══██╔══╝██║
 * ██║  ███╗██║        ██║   ██║
 * ██║   ██║██║        ██║   ██║
 * ╚██████╔╝╚██████╗   ██║   ███████╗
 *  ╚═════╝  ╚═════╝   ╚═╝   ╚══════╝
 * Geophysical Computational Tools & Library (GCTL)
 *
 * Copyright (c) 2023  Yi Zhang (yizhang-geo@zju.edu.cn)
 *
 * GCTL is distributed under a dual licensing scheme. You can redistribute 
 * it and/or modify it under the terms of the GNU Lesser General Public 
 * License as published by the Free Software Foundation, either version 2 
 * of the License, or (at your option) any later version. You should have 
 * received a copy of the GNU Lesser General Public License along with this 
 * program. If not, see <http://www.gnu.org/licenses/>.
 * 
 * If the terms and conditions of the LGPL v.2. would prevent you from using 
 * the GCTL, please consider the option to obtain a commercial license for a 
 * fee. These licenses are offered by the GCTL's original author. As a rule, 
 * licenses are provided "as-is", unlimited in time for a one time fee. Please 
 * send corresponding requests to: yizhang-geo@zju.edu.cn. Please do not forget 
 * to include some description of your company and the realm of its activities. 
 * Also add information on how to contact you by electronic and paper mail.
 ******************************************************/

#ifndef _GCTL_TEXT_IO_H
#define _GCTL_TEXT_IO_H

// library's head file
#include "../core.h"
#include "../utility.h"
#include "../geometry.h"
// system head file
#include "initializer_list"

namespace gctl
{
	struct text_descriptor
	{
		int head_num_;
		// 注释行起始符 标记行起始符 分割符
		char att_sym_, tag_sym_, delimiter_;
		// 文件扩展名
		std::string file_name_, file_ext_;
		// 注释行 标记行
		std::vector<std::string> head_strs_, annotates_, tags_;

		text_descriptor();

		/**
		 * @brief Construct a new text descriptor object
		 * 
		 * @param filename 文件名
		 * @param file_exten 文件扩展名
		 * @param att_sym 注释行起始符
		 * @param tag_sym 标记行起始符
		 * @param deli 分割符
		 */
		text_descriptor(std::string filename, std::string file_exten = ".txt", char att_sym = '#', char tag_sym = '>', char deli = ' ', int h_num = 0);

		~text_descriptor();
		
		/**
		 * @brief 设置文件对象
		 * 
		 * @param filename 文件名
		 * @param file_exten 文件扩展名
		 * @param att_sym 注释行起始符
		 * @param tag_sym 标记行起始符
		 * @param deli 分割符
		 */
		void set(std::string filename = "Untitled", std::string file_exten = ".txt", char att_sym = '#', char tag_sym = '>', char deli = ' ', int h_num = 0);

		void clear();
	};

	/**
	 * @brief      从文本文件读入行到一维向量
	 * 
	 * @note       文件中的每一行为向量中的一个元素，对应的元素类型需支持>>操作符（一般通过操作符重载实现）
	 *
	 * @param[in]  filename     文件名
	 * @param[in]  file_ext     文件后缀名（默认包括txt dat xyz csv）
	 * @param      out_vec      返回的一维向量
	 * @param[in]  annotate     注释行的标记
	 * @param[in]  head_record  头信息行数
	 * @param      head_ptr     保存头信息的一维字符串向量的指针
	 *
	 * @tparam     T            模版向量类型
	 */
	template <typename T>
	void read_text2vector(std::string filename, std::vector<T> &out_vec, char annotate = '#', unsigned int head_record = 0, 
		std::vector<std::string> *head_ptr = nullptr, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::string tmp_line;
		std::stringstream tmp_ss;

		std::ifstream infile;
		open_matched_infile(infile, filename, file_ext);

		// clear cleanup old table
		if (!out_vec.empty()) destroy_vector(out_vec);
		if (head_ptr != nullptr && !head_ptr->empty()) destroy_vector(*head_ptr);

		if (head_record > 0)
		{
			for (int i = 0; i < head_record; i++)
			{
				getline(infile, tmp_line);
				if (head_ptr != nullptr) head_ptr->push_back(tmp_line);
			}
		}

		T tmp_d;
		while (std::getline(infile, tmp_line))
		{
			if (tmp_line[0] == annotate) continue;
			else
			{
				tmp_ss.clear();
				tmp_ss.str(tmp_line);
				tmp_ss >> tmp_d;
				if (tmp_ss.fail())
				{
					throw runtime_error("Fail to parse the element: "+tmp_line+". From read_text2vector(...)");
				}

				out_vec.push_back(tmp_d);
			}
		}

		infile.close();
		return;
	}

	template <typename T>
	void read_text2array(std::string filename, array<T> &out_arr, char annotate = '#', unsigned int head_record = 0, 
		array<std::string> *head_ptr = nullptr, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::string tmp_line;
		std::stringstream tmp_ss;

		std::ifstream infile;
		open_matched_infile(infile, filename, file_ext);

		// clear cleanup old table
		if (!out_arr.empty()) out_arr.clear();
		if (head_ptr != nullptr && !head_ptr->empty()) head_ptr->clear();
		if (head_ptr != nullptr && head_record > 0) head_ptr->resize(head_record);

		int line_count = 0, annotate_count = 0;
		while (std::getline(infile, tmp_line))
		{
			line_count++;
			if (tmp_line[0] == annotate) annotate_count++;
		}

		// reset the file pointer
		infile.clear(); infile.seekg(0);

		if (line_count == 0)
		{
			throw length_error("File: " + filename + " is empty. From read_text2array(...)");
		}

		int arr_size = line_count - annotate_count - head_record;

		if (arr_size <= 0)
		{
			throw length_error("No valid entrance in the file: " + filename + " From read_text2array(...)");
		}

		out_arr.resize(arr_size);
		
		int count = 0;
		if (head_record > 0)
		{
			while (count < head_record)
			{
				std::getline(infile, tmp_line);
				if (tmp_line[0] == annotate) continue;
				else
				{
					if (head_ptr != nullptr) head_ptr->at(count) = tmp_line;
					count++;
				}
			}
		}
		
		T tmp_d;
		count = 0;
		while (std::getline(infile, tmp_line))
		{
			if (tmp_line[0] == annotate) continue;
			else
			{
				tmp_ss.clear();
				tmp_ss.str(tmp_line);
				tmp_ss >> tmp_d;
				if (tmp_ss.fail())
				{
					throw runtime_error("Fail to parse the element: "+tmp_line+". From read_text2array(...)");
				}

				out_arr[count] = tmp_d;
				count++;
			}
		}

		infile.close();
		return;
	}

	/**
	 * @brief      保存一维向量到文本文件
	 * 
	 * @note       向量中的每个元素保存为一行，对应的元素类型需支持<<操作符（一般通过操作符重载实现）
	 *
	 * @param[in]  filename   保存文件名
	 * @param[in]  file_ext   保存文件的后缀
	 * @param[in]  in_vec     待输出的向量
	 * @param[in]  annotate   注释行的标记
	 * @param[in]  head_info  头信息向量的指针
	 * @param[in]  precision  设置输出的数据精度，此值为正时将在<<操作符后附加setprecision(precision)代码段
	 *
	 * @tparam     T          模版向量类型
	 */
	template <typename T>
	void save_vector2text(std::string filename, const std::vector<T> &in_vec, char annotate = '#', 
		std::vector<std::string> *head_ptr = nullptr, int precision = -1, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, file_ext);

		if (in_vec.empty())
		{
			GCTL_ShowWhatError("The input vector is empty. From save_vector2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (head_ptr != nullptr)
		{
			for (int i = 0; i < head_ptr->size(); ++i)
			{
				outfile << annotate << " " << head_ptr->at(i) << std::endl;
			}
		}

		if (precision > 0)
		{
			for (int i = 0; i < in_vec.size(); i++)
			{
				outfile << std::setprecision(precision) << in_vec[i] << std::endl;
			}
			outfile.close();
			return;
		}

		for (int i = 0; i < in_vec.size(); i++)
		{
			outfile << in_vec[i] << std::endl;
		}
		outfile.close();
		return;
	}

	/**
	 * @brief      保存一维数组到文本文件
	 * 
	 * @note       数组的每个元素保存为一行，对应的元素类型需支持<<操作符（一般通过操作符重载实现）
	 *
	 * @param[in]  filename   保存文件名
	 * @param[in]  file_ext   保存文件的后缀
	 * @param[in]  in_arr     待输出的向量
	 * @param[in]  annotate   注释行的标记
	 * @param[in]  head_info  头信息向量的指针
	 * @param[in]  precision  设置输出的数据精度，此值为正时将在<<操作符后附加setprecision(precision)代码段
	 *
	 * @tparam     T          模版向量类型
	 */
	template <typename T>
	void save_array2text(std::string filename, const array<T> &in_arr, const text_descriptor &desc, 
		int precision = -1, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, file_ext);

		if (in_arr.empty())
		{
			GCTL_ShowWhatError("The input array is empty. From save_vector2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		for (int i = 0; i < desc.annotates_.size(); ++i)
		{
			outfile << desc.annotates_[i] << std::endl;
		}

		if (precision > 0)
		{
			for (int i = 0; i < in_arr.size(); i++)
			{
				outfile << std::fixed << std::setprecision(precision) << in_arr[i] << std::endl;
			}
			outfile.close();
			return;
		}

		for (int i = 0; i < in_arr.size(); i++)
		{
			outfile << in_arr[i] << std::endl;
		}
		outfile.close();
		return;
	}

	template <typename T>
	void read_text2vector2d(std::string filename, std::vector<std::vector<T>> &out_vec2d, text_descriptor &desc, std::string file_ext = ".txt,.dat,.xyz,.csv,.tab")
	{
		std::ifstream infile;
		open_matched_infile(infile, filename, file_ext);

		// clear old table
		if (!out_vec2d.empty()) destroy_vector(out_vec2d);
		desc.annotates_.reserve(1024);
		desc.head_strs_.reserve(1024);
		desc.tags_.reserve(1024);

		std::string tmp_line;
		std::vector<T> tmp_row;

		for (size_t i = 0; i < desc.head_num_; i++)
		{
			getline(infile, tmp_line);
			desc.head_strs_.push_back(tmp_line);
		}

		while (getline(infile, tmp_line))
		{
			if (tmp_line[0] == desc.att_sym_)
			{
				desc.annotates_.push_back(tmp_line);
			}
			else if (tmp_line[0] == desc.tag_sym_)
			{
				desc.tags_.push_back(tmp_line);
			}
			else
			{
				tmp_row.clear();
				parse_string_to_vector(tmp_line, desc.delimiter_, tmp_row);
				out_vec2d.push_back(tmp_row);
			}
		}

		infile.close();
		return;
	}

	template <typename T>
	void save_vector2d2text(std::string filename, const std::vector<std::vector<T>> &out_vec2d, 
		char delimiter = ' ', char annotate = '#', std::vector<std::string> *head_ptr = nullptr, 
		matrix_order_e order_type = RowMajor, int precision = -1, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, file_ext);

		if (out_vec2d.empty())
		{
			GCTL_ShowWhatError("The input vector is empty. From save_vector2d2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (head_ptr != nullptr)
		{
			for (int i = 0; i < head_ptr->size(); i++)
			{
				outfile << annotate << " " << head_ptr->at(i) << std::endl;
			}
		}

		if (order_type == RowMajor)
		{
			if (precision > 0)
			{
				for (int i = 0; i < out_vec2d.size(); i++)
				{
					outfile << std::setprecision(precision) << out_vec2d[i][0];
					for (int j = 1; j < out_vec2d.at(i).size(); j++)
					{
						outfile << std::setprecision(precision) << delimiter << out_vec2d[i][j];
					}
					outfile << std::endl;
				}

				outfile.close();
				return;
			}

			for (int i = 0; i < out_vec2d.size(); i++)
			{
				outfile << out_vec2d[i][0];
				for (int j = 1; j < out_vec2d.at(i).size(); j++)
				{
					outfile << delimiter << out_vec2d[i][j];
				}
				outfile << std::endl;
			}

			outfile.close();
			return;
		}

		if (precision > 0)
		{
			for (int i = 0; i < out_vec2d[0].size(); i++)
			{
				outfile << std::setprecision(precision) << out_vec2d[0][i];
				for (int j = 1; j < out_vec2d.size(); j++)
				{
					outfile << std::setprecision(precision) << delimiter << out_vec2d[j][i];
				}
				outfile << std::endl;
			}

			outfile.close();
			return;
		}

		for (int i = 0; i < out_vec2d[0].size(); i++)
		{
			outfile << out_vec2d[0][i];
			for (int j = 1; j < out_vec2d.size(); j++)
			{
				outfile << delimiter << out_vec2d[j][i];
			}
			outfile << std::endl;
		}

		outfile.close();
		return;
	}

	template <typename T>
	void save_matrix2text(std::string filename, const matrix<T> &out_arr2d, 
		char delimiter = ' ', char annotate = '#', std::vector<std::string> *head_ptr = nullptr, 
		matrix_order_e order_type = RowMajor, int precision = -1, std::string file_ext = ".txt,.dat,.xyz,.csv")
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, file_ext);

		if (out_arr2d.empty())
		{
			GCTL_ShowWhatError("The input array is empty. From save_matrix2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (head_ptr != nullptr)
		{
			for (int i = 0; i < head_ptr->size(); i++)
			{
				outfile << annotate << " " << head_ptr->at(i) << std::endl;
			}
		}

		if (order_type == RowMajor)
		{
			if (precision > 0)
			{
				for (int i = 0; i < out_arr2d.row_size(); i++)
				{
					outfile << std::setprecision(precision) << out_arr2d[i][0];
					for (int j = 1; j < out_arr2d.col_size(); j++)
					{
						outfile << std::setprecision(precision) << delimiter << out_arr2d.at(i, j);
					}
					outfile << std::endl;
				}

				outfile.close();
				return;
			}

			for (int i = 0; i < out_arr2d.row_size(); i++)
			{
				outfile << out_arr2d[i][0];
				for (int j = 1; j < out_arr2d.col_size(); j++)
				{
					outfile << delimiter << out_arr2d.at(i, j);
				}
				outfile << std::endl;
			}

			outfile.close();
			return;
		}

		if (precision > 0)
		{
			for (int i = 0; i < out_arr2d.col_size(); i++)
			{
				outfile << std::setprecision(precision) << out_arr2d[0][i];
				for (int j = 1; j < out_arr2d.row_size(); j++)
				{
					outfile << std::setprecision(precision) << delimiter << out_arr2d.at(j, i);
				}
				outfile << std::endl;
			}

			outfile.close();
			return;
		}

		for (int i = 0; i < out_arr2d.col_size(); i++)
		{
			outfile << out_arr2d[0][i];
			for (int j = 1; j < out_arr2d.row_size(); j++)
			{
				outfile << delimiter << out_arr2d.at(j, i);
			}
			outfile << std::endl;
		}

		outfile.close();
		return;
	}

	template <typename T>
	void read_text2vectors(std::string filename, std::string order_str, 
		char delimiter, char annotate, int head_record, std::vector<T> &out_vec)
	{
		std::string tmp_line, tmp_str, tmp_str2;
		std::stringstream tmp_ss;

		std::ifstream infile;
		open_infile(infile, filename);

		// clear cleanup old table
		if (!out_vec.empty()) destroy_vector(out_vec);

		tmp_ss.clear();
		tmp_ss.str(order_str); // get the first index separated by comma
		std::getline(tmp_ss, tmp_str, ',');
		// get input column index
		int col_id;
		tmp_ss.clear();
		tmp_ss.str(tmp_str);
		tmp_ss >> col_id;

		if (col_id < 0)
		{
			throw runtime_error("Invalid column index. From read_text2vectors(...)");
		}

		if (head_record > 0)
		{
			for (int i = 0; i < head_record; i++)
			{
				getline(infile, tmp_line);
			}
		}

		T tmp_d;
		if (delimiter != ' ')
		{
			while (std::getline(infile, tmp_line))
			{
				if (tmp_line[0] == annotate) continue;
				else
				{
					// replace all delimiters to spaces
					tmp_str2 = delimiter;
					replace_all(tmp_str, tmp_line, tmp_str2, " ");
					tmp_ss.clear();
					tmp_ss.str(tmp_str);
					// Skip the first col_id elements
					for (int i = 0; i < col_id; i++)
					{
						tmp_ss >> tmp_str2;
					}

					// convert the target object
					tmp_ss >> tmp_d;
					if (tmp_ss.fail())
					{
						throw runtime_error("Fail to parse the line: "+tmp_line+". From read_text2vectors(...)");
					}

					out_vec.push_back(tmp_d);
				}
			}
		}
		else
		{
			while (std::getline(infile, tmp_line))
			{
				if (tmp_line[0] == annotate) continue;
				else
				{
					tmp_ss.clear();
					tmp_ss.str(tmp_line);
					// Skip the first col_id elements
					for (int i = 0; i < col_id; i++)
					{
						tmp_ss >> tmp_str2;
					}

					// convert the target object
					tmp_ss >> tmp_d;
					if (tmp_ss.fail())
					{
						throw runtime_error("Fail to parse the line: "+tmp_line+". From read_text2vectors(...)");
					}

					out_vec.push_back(tmp_d);
				}
			}
		}

		infile.close();
		return;
	}

	template <typename T, typename... Args>
	void read_text2vectors(std::string filename, std::string order_str, char delimiter, 
		char annotate, int head_record, std::vector<T> &out_vec, std::vector<Args>&... rest)
	{
		std::string tmp_line, tmp_str, tmp_str2;
		std::stringstream tmp_ss;

		std::ifstream infile;
		open_infile(infile, filename);

		// clear cleanup old table
		if (!out_vec.empty()) destroy_vector(out_vec);

		tmp_ss.clear();
		tmp_ss.str(order_str); // get the first index separated by comma
		std::getline(tmp_ss, tmp_str, ',');
		// get input column index
		int col_id;
		tmp_ss.clear();
		tmp_ss.str(tmp_str);
		tmp_ss >> col_id;

		if (col_id < 0)
		{
			throw runtime_error("Invalid column index. From read_text2vectors(...)");
		}

		int position = order_str.find_first_of(',');
		std::string tmp_order = order_str.substr(position+1, order_str.length());

		if (head_record > 0)
		{
			for (int i = 0; i < head_record; i++)
			{
				getline(infile, tmp_line);
			}
		}

		T tmp_d;
		if (delimiter != ' ')
		{
			while (std::getline(infile, tmp_line))
			{
				if (tmp_line[0] == annotate) continue;
				else
				{
					// replace all delimiters to spaces
					tmp_str2 = delimiter;
					replace_all(tmp_str, tmp_line, tmp_str2, " ");
					tmp_ss.clear();
					tmp_ss.str(tmp_str);
					// Skip the first col_id elements
					for (int i = 0; i < col_id; i++)
					{
						tmp_ss >> tmp_str2;
					}

					// convert the target object
					tmp_ss >> tmp_d;
					if (tmp_ss.fail())
					{
						throw runtime_error("Fail to parse the line: "+tmp_line+". From read_text2vectors(...)");
					}

					out_vec.push_back(tmp_d);
				}
			}
		}
		else
		{
			while (std::getline(infile, tmp_line))
			{
				if (tmp_line[0] == annotate) continue;
				else
				{
					tmp_ss.clear();
					tmp_ss.str(tmp_line);
					// Skip the first col_id elements
					for (int i = 0; i < col_id; i++)
					{
						tmp_ss >> tmp_str2;
					}

					// convert the target object
					tmp_ss >> tmp_d;
					if (tmp_ss.fail())
					{
						throw runtime_error("Fail to parse the line: "+tmp_line+". From read_text2vectors(...)");
					}

					out_vec.push_back(tmp_d);
				}
			}
		}

		infile.close();
		read_text2vectors(filename, tmp_order, delimiter, annotate, head_record, rest...);
		return;
	}

	template <typename T>
	void save_vectors2text(std::string filename, char delimiter, char annotate, 
		std::vector<std::string> *head_ptr, std::vector<std::string> *content_ptr, 
		const std::vector<T> &in_vec)
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, ".txt,.dat,.xyz,.csv");

		if (in_vec.empty())
		{
			GCTL_ShowWhatError("The input vector is empty. From save_vectors2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (content_ptr == nullptr)
		{
			content_ptr = new std::vector<std::string>(in_vec.size());
			for (int i = 0; i < content_ptr->size(); i++)
			{
				content_ptr->at(i) = "";
			}
		}

		int old_row = content_ptr->size();
		int new_row = in_vec.size();

		// add in_vec to content_ptr
		std::string tmp_str, tmp_str2, nan_str = "nan";
		std::stringstream tmp_ss;

		if (old_row < new_row) // 当前输入的数组长度长于现有的字符串数组长度 在当前字符串后面补足
		{
			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << content_ptr->at(0);
			while (std::getline(tmp_ss, tmp_str, delimiter))
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = 0; i < (new_row-old_row); i++)
			{
				content_ptr->push_back(tmp_str2);
			}

			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}
		else if (old_row > new_row)
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}

			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << in_vec[0];
			while(tmp_ss >> tmp_str)
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = new_row; i < old_row; i++)
			{
				content_ptr->at(i) += tmp_str2;
			}
		}
		else
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}

		// start writing file
		if (head_ptr != nullptr)
		{
			for (int i = 0; i < head_ptr->size(); ++i)
			{
				outfile << annotate << " " << head_ptr->at(i) << std::endl;
			}
		}

		for (int i = 0; i < content_ptr->size(); i++)
		{
			tmp_str = content_ptr->at(i).substr(0, content_ptr->at(i).length()-1);
			outfile << content_ptr->at(i) << std::endl;
		}

		destroy_vector(*content_ptr);
		outfile.close();
		return;
	}

	template <typename T, typename... Args>
	void save_vectors2text(std::string filename, char delimiter, char annotate, 
		std::vector<std::string> *head_ptr, std::vector<std::string> *content_ptr, 
		const std::vector<T> &in_vec, const std::vector<Args>&... rest)
	{
		if (in_vec.empty())
		{
			GCTL_ShowWhatError("The input vector is empty. From save_vectors2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (content_ptr == nullptr)
		{
			content_ptr = new std::vector<std::string>(in_vec.size());
			for (int i = 0; i < content_ptr->size(); i++)
			{
				content_ptr->at(i) = "";
			}
		}

		int old_row = content_ptr->size();
		int new_row = in_vec.size();

		// add in_vec to content_ptr
		std::string tmp_str, tmp_str2, nan_str = "nan";
		std::stringstream tmp_ss;

		if (old_row < new_row) // 当前输入的数组长度长于现有的字符串数组长度 在当前字符串后面补足
		{
			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << content_ptr->at(0);
			while (std::getline(tmp_ss, tmp_str, delimiter))
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = 0; i < (new_row-old_row); i++)
			{
				content_ptr->push_back(tmp_str2);
			}

			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}
		else if (old_row > new_row)
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}

			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << in_vec[0];
			while(tmp_ss >> tmp_str)
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = new_row; i < old_row; i++)
			{
				content_ptr->at(i) += tmp_str2;
			}
		}
		else
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}

		save_vectors2text(filename, delimiter, annotate, head_ptr, content_ptr, rest...);
		return;
	}

	template <typename T>
	void save_arrays2text(std::string filename, char delimiter, char annotate, 
		std::vector<std::string> *head_ptr, std::vector<std::string> *content_ptr, 
		const array<T> &in_vec)
	{
		std::ofstream outfile;
		open_matched_outfile(outfile, filename, ".txt,.dat,.xyz,.csv");

		if (in_vec.empty())
		{
			GCTL_ShowWhatError("The input array is empty. From save_arrays2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (content_ptr == nullptr)
		{
			content_ptr = new std::vector<std::string>(in_vec.size());
			for (int i = 0; i < content_ptr->size(); i++)
			{
				content_ptr->at(i) = "";
			}
		}

		int old_row = content_ptr->size();
		int new_row = in_vec.size();

		// add in_vec to content_ptr
		std::string tmp_str, tmp_str2, nan_str = "nan";
		std::stringstream tmp_ss;

		if (old_row < new_row) // 当前输入的数组长度长于现有的字符串数组长度 在当前字符串后面补足
		{
			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << content_ptr->at(0);
			while (std::getline(tmp_ss, tmp_str, delimiter))
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = 0; i < (new_row-old_row); i++)
			{
				content_ptr->push_back(tmp_str2);
			}

			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}
		else if (old_row > new_row)
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}

			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << in_vec[0];
			while(tmp_ss >> tmp_str)
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = new_row; i < old_row; i++)
			{
				content_ptr->at(i) += tmp_str2;
			}
		}
		else
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}

		if (head_ptr != nullptr)
		{
			for (int i = 0; i < head_ptr->size(); ++i)
			{
				outfile << annotate << " " << head_ptr->at(i) << std::endl;
			}
		}

		for (int i = 0; i < content_ptr->size(); i++)
		{
			tmp_str = content_ptr->at(i).substr(0, content_ptr->at(i).length()-1);
			outfile << tmp_str << std::endl;
		}

		destroy_vector(*content_ptr);
		outfile.close();
		return;
	}

	template <typename T, typename... Args>
	void save_arrays2text(std::string filename, char delimiter, char annotate, 
		std::vector<std::string> *head_ptr, std::vector<std::string> *content_ptr, 
		const array<T> &in_vec, const array<Args>&... rest)
	{
		if (in_vec.empty())
		{
			GCTL_ShowWhatError("The input array is empty. From save_arrays2text(...)", GCTL_WARNING_ERROR, 0, 0, 0);
		}

		if (content_ptr == nullptr)
		{
			content_ptr = new std::vector<std::string>(in_vec.size());
			for (int i = 0; i < content_ptr->size(); i++)
			{
				content_ptr->at(i) = "";
			}
		}

		int old_row = content_ptr->size();
		int new_row = in_vec.size();

		// add in_vec to content_ptr
		std::string tmp_str, tmp_str2, nan_str = "nan";
		std::stringstream tmp_ss;

		if (old_row < new_row) // 当前输入的数组长度长于现有的字符串数组长度 在当前字符串后面补足
		{
			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << content_ptr->at(0);
			while (std::getline(tmp_ss, tmp_str, delimiter))
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = 0; i < (new_row-old_row); i++)
			{
				content_ptr->push_back(tmp_str2);
			}

			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}
		else if (old_row > new_row)
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}

			tmp_str2 = "";
			tmp_ss.clear();
			tmp_ss << in_vec[0];
			while(tmp_ss >> tmp_str)
			{
				tmp_str2 += (nan_str + delimiter);
			}

			for (int i = new_row; i < old_row; i++)
			{
				content_ptr->at(i) += tmp_str2;
			}
		}
		else
		{
			for (int i = 0; i < new_row; i++)
			{
				tmp_ss.clear();
				tmp_ss << in_vec[i];
				while(tmp_ss >> tmp_str)
				{
					content_ptr->at(i) += (tmp_str + delimiter);
				}
			}
		}

		save_arrays2text(filename, delimiter, annotate, head_ptr, content_ptr, rest...);
		return;
	}

	/**
	 * @brief 从文本文件中读入指定的行到字符串数组
	 * 
	 * @param file_desc 文件描述对象
	 * @param out_vec 返回的字符串数组
	 * @param start_line 起始行，若start_line和end_line同为0则读取整个文件
	 * @param end_line 终止行，若start_line和end_line同为0则读取整个文件
	 */
	void read_text_lines(text_descriptor &file_desc, std::vector<std::string> &out_vec, int start_line = 0, int end_line = 0);

	/**
	 * @brief      从文本文件中读入注释行到字符串数组
	 * 
	 * @note       读取的字符串不包含标志符
	 *
	 * @param[in]  filename  文件名
	 * @param[in]  file_ext  文件名后缀
	 * @param      out_vec   返回的字符串数组
	 * @param[in]  annotate  注释行的标志符，默认为#号
	 */
	void read_annotations(std::string filename, std::vector<std::string> &out_vec, std::string file_ext = ".txt", 
		char annotate = '#');

	/**
	 * @brief      从二维向量读入直角坐标系的点数组
	 *
	 * @param[in]  data_table  二维向量
	 * @param      out_ps      返回的点数组
	 * @param[in]  order       三维坐标的点列序列
	 */
	void get_xyz_points(const _2d_vector &data_table, array<point3dc> &out_ps, std::string order = "0,1,2");

	/**
	 * @brief      从二维向量读入直角坐标系的点数组
	 *
	 * @param[in]  data_table  二维向量
	 * @param      out_ps      返回的点数组
	 * @param[in]  order       三维坐标的点列序列
	 */
	void get_xyz_points(const _2d_vector &data_table, array<point3ds> &out_ps, std::string order = "0,1,2");

	/**
	 * @brief      从文件读入直角坐标系的点数组
	 *
	 * @param[in]  filename   文件名
	 * @param      out_ps     返回的点数组
	 * @param[in]  order      三维坐标的点列序列
	 * @param[in]  delimiter  分隔符
	 * @param[in]  annotate   标注行符号
	 */
	void get_xyz_points(std::string filename, array<point3dc> &out_ps, text_descriptor &desc, 
		std::string order = "0,1,2", std::string file_ext = ".txt,.dat,.xyz,.csv");

	/**
	 * @brief      从文件读入球坐标系的点数组
	 *
	 * @param[in]  filename   文件名
	 * @param      out_ps     返回的点数组
	 * @param[in]  order      三维坐标的点列序列
	 * @param[in]  delimiter  分隔符
	 * @param[in]  annotate   标注行符号
	 */
	void get_xyz_points(std::string filename, array<point3ds> &out_ps, text_descriptor &desc, 
		std::string order = "0,1,2", std::string file_ext = ".txt,.dat,.xyz,.csv");

	/**
	 * @brief      读取数据列
	 *
	 * @param[in]  data_table  二维数据向量
	 * @param[in]  dat_val     输入的数据数组列表
	 * @param[in]  dat_col     输入的数据列（数据列不足时会按从左到右顺序读入）
	 */
	void get_data_column(const _2d_vector &data_table, std::initializer_list<array<double>*> dat_val, 
		std::initializer_list<int> dat_col);

	/**
	 * @brief      读取数据列
	 *
	 * @param      filename     输入文件名
	 * @param[in]  dat_val      输入的数据数组列表
	 * @param[in]  dat_col      输入的数据列（数据列不足时会按从左到右顺序读入）
	 * @param[in]  delimiter    元素分割符
	 * @param[in]  annotate     注释行符号
	 * @param[in]  head_record  头信息行数
	 */
	void get_data_column(std::string filename, std::initializer_list<array<double>*> dat_val, 
		std::initializer_list<int> dat_col, text_descriptor &desc, std::string file_ext = ".txt,.dat,.xyz,.csv");

	/**
	 * @brief      保存数据列
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dat_val  输出的数据数组列表
	 */
	void save_data_column(std::ofstream &outfile, std::initializer_list<array<double>*> dat_val, 
		std::initializer_list<std::string> dat_name, char delimiter = ' ', int precision = GCTL_PRECISION);

	/**
	 * @brief      保存数据列（std向量类型的重载）
	 *
	 * @param      outfile   输出文件流
	 * @param[in]  dat_val  输出的数据数组列表
	 */
	void save_data_column(std::ofstream &outfile, std::initializer_list<std::vector<double>*> dat_val, 
		std::initializer_list<std::string> dat_name, char delimiter = ' ', int precision = GCTL_PRECISION);
}

#endif //_GCTL_TEXT_IO_H
